/*
 *    Copyright 2011 The gcal-to-refill project
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 */
package com.googlecode.gcal2refill;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;

import com.google.gdata.client.authn.oauth.GoogleOAuthParameters;
import com.google.gdata.data.calendar.CalendarEntry;
import com.google.gdata.data.calendar.CalendarEventEntry;
import com.itextpdf.text.BaseColor;
import com.itextpdf.text.Document;
import com.itextpdf.text.DocumentException;
import com.itextpdf.text.Element;
import com.itextpdf.text.Font;
import com.itextpdf.text.PageSize;
import com.itextpdf.text.Phrase;
import com.itextpdf.text.Rectangle;
import com.itextpdf.text.pdf.BaseFont;
import com.itextpdf.text.pdf.PdfContentByte;
import com.itextpdf.text.pdf.PdfPCell;
import com.itextpdf.text.pdf.PdfPTable;
import com.itextpdf.text.pdf.PdfWriter;

public class PdfCreator {
    public static Rectangle[] pageSize;
    public static BaseFont jbf;
    public static BaseFont bf;

    static {
        pageSize =
            new Rectangle[] {
                PageSize.A4,
                PageSize.A5,
                new Rectangle(
                    0F,
                    0F,
                    Util.mm2point(95.0F),
                    Util.mm2point(170.0F)),
                PageSize.A6 };
    }

    static {
        try {
            jbf =
                BaseFont.createFont("HeiseiKakuGo-W5", "UniJIS-UCS2-H", false);
            bf =
                BaseFont.createFont(
                    BaseFont.TIMES_ROMAN,
                    BaseFont.CP1250,
                    false);
        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public byte[] createPdf(UserInput ui, GoogleOAuthParameters params) {
        float tojisiro = 0F;
        if (ui.isTojisiro()) {
            tojisiro = Util.mm2point(ui.getTojisiroSize());
        }
        Document document =
            new Document(pageSize[ui.getSizeOfPaper()], 0.0F, 0.0F, 0.0F, 0.0F);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            PdfWriter writer = PdfWriter.getInstance(document, baos);
            document.open();
            createCover(ui, document, writer);
            EventManager em = new EventManager();
            GcalAccessor gcalAccess = new GcalAccessor(ui, params);
            List<CalendarEntry> calEntryList = gcalAccess.getCalenderList();
            for (Iterator<CalendarEntry> iterator = calEntryList.iterator(); iterator
                .hasNext();) {
                CalendarEntry entry = iterator.next();
                TimeZone.setDefault(TimeZone.getTimeZone(entry.getTimeZone().getValue()));
                boolean isOutput = false;
                if (ui.getSelectedCalendar() != null){
                    for (int i = 0; i < ui.getSelectedCalendar().length; ++i) {
                        if (ui.getSelectedCalendar()[i].equals(entry
                            .getTitle()
                            .getPlainText())) {
                            isOutput = true;
                            break;
                        }
                    }
                    if (isOutput) {
                        List<CalendarEventEntry> eventEntryList =
                            gcalAccess.getCalendarEventEntryList(entry);
                        BaseColor col = Util.getColor(entry.getColor().getValue());
                        em.setCalendarEvent(eventEntryList, col, ui);
                    }
                }
            }
            for (Iterator<CalendarEntry> iterator = calEntryList.iterator(); iterator
                .hasNext();) {
                CalendarEntry entry = iterator.next();
                boolean isHolidayCal = false;
                if (ui.getHolidayCalendar() != null){
                    for (int i = 0; i < ui.getHolidayCalendar().length; ++i) {
                        if (ui.getHolidayCalendar()[i].equals(entry
                            .getTitle()
                            .getPlainText())) {
                            isHolidayCal = true;
                            break;
                        }
                    }
                    if (isHolidayCal) {
                        List<CalendarEventEntry> eventEntryList =
                            gcalAccess.getCalendarEventEntryList(entry);
                        BaseColor col = Util.getColor(entry.getColor().getValue());
                        em.setCalendarEventHoliday(eventEntryList, col);
                    }
                }
            }
            Calendar startData = Calendar.getInstance();
            startData.setTime(ui.getStartDate());
            Calendar createCal = Calendar.getInstance();
            createCal.setTime(startData.getTime());
            OneDayAreaInfo odpoint = new OneDayAreaInfo(ui);
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM");
            PdfPCell[][] cells = new PdfPCell[ui.getWeekPeriod() * 8][2];
            for (int i = 0; i < ui.getWeekPeriod(); i++) {
                PdfPCell monthCell =
                    new PdfPCell(new Phrase(
                        sdf.format(createCal.getTime()),
                        new Font(jbf, 14F, Font.BOLD, Constant.WEEKDAY_COLOR)));
                monthCell.setBorder(Rectangle.NO_BORDER);
                monthCell.setHorizontalAlignment(Element.ALIGN_LEFT);
                monthCell.setVerticalAlignment(Element.ALIGN_BOTTOM);
                cells[i * 8][0] = monthCell;
                cells[i * 8][1] = new PdfPCell();
                cells[i * 8][1].setBorder(Rectangle.NO_BORDER);
                for (int j = 1; j < 8; j++) {
                    cells[i * 8 + j][0] =
                        new PdfPCell(createDayInfo(
                            ui,
                            odpoint,
                            em,
                            createCal.getTime()));
                    cells[i * 8 + j][0]
                        .setFixedHeight(OneDayAreaInfo.DAYINFO_HEIGHT);
                    cells[i * 8 + j][1] =
                        createDayEvent(odpoint, em, createCal.getTime());
                    createCal.add(Calendar.DAY_OF_YEAR, 1);
                }
            }
            startData = Calendar.getInstance();
            startData.setTime(ui.getStartDate());
            createCal = Calendar.getInstance();
            createCal.setTime(startData.getTime());
            for (int i = 0; i < ui.getWeekPeriod(); i++) {
                PdfPTable table =
                    createLeftPage(ui, odpoint, document, cells, i);
                table.writeSelectedRows(
                    0,
                    -1,
                    0,
                    pageSize[ui.getSizeOfPaper()].getHeight(),
                    writer.getDirectContent());
                plotScheduleLeftPage(ui, em, odpoint, createCal, writer);
                document.newPage();
                table = createRightPage(ui, odpoint, document, cells, i);
                table.writeSelectedRows(
                    0,
                    -1,
                    tojisiro,
                    pageSize[ui.getSizeOfPaper()].getHeight(),
                    writer.getDirectContent());
                plotScheduleRightPage(ui, em, odpoint, createCal, writer);
                document.newPage();
            }

        } catch (DocumentException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
        document.close();
        return baos.toByteArray();
    }

    private void createCover(UserInput userIn, Document document,
            PdfWriter writer) {
        PdfContentByte coverTitleCb = writer.getDirectContent();
        coverTitleCb.beginText();
        coverTitleCb.setFontAndSize(jbf, 15F);
        coverTitleCb.showTextAligned(
            PdfContentByte.ALIGN_CENTER,
            "Weekly Schedule",
            document.getPageSize().getWidth() / 2F,
            document.getPageSize().getHeight() / 2 + 17F,
            0F);
        coverTitleCb.endText();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        String startDate = sdf.format(userIn.getStartDate());
        Calendar endDateCal = Calendar.getInstance();
        endDateCal.setTime(userIn.getStartDate());
        endDateCal.add(Calendar.DAY_OF_YEAR, userIn.getWeekPeriod() * 7);
        String endDate = sdf.format(endDateCal.getTime());
        PdfContentByte coverSubTitleCb = writer.getDirectContent();
        coverSubTitleCb.beginText();
        coverSubTitleCb.setFontAndSize(jbf, 12F);
        coverSubTitleCb.showTextAligned(PdfContentByte.ALIGN_CENTER, startDate
            + " ～ "
            + endDate, document.getPageSize().getWidth() / 2F, document
            .getPageSize()
            .getHeight() / 2, 0F);
        coverTitleCb.endText();
        String printDate = sdf.format(new Date());
        PdfContentByte coverCreatedCb = writer.getDirectContent();
        coverCreatedCb.beginText();
        coverCreatedCb.setFontAndSize(jbf, 8F);
        coverCreatedCb.showTextAligned(
            PdfContentByte.ALIGN_LEFT,
            "https://gcal2refill.appspot.com/gcal2refill",
            Util.mm2point(userIn.getTojisiroSize()) + 10F,
            10F,
            0F);
        coverCreatedCb.showTextAligned(
            PdfContentByte.ALIGN_RIGHT,
            "Create Date:" + printDate,
            document.getPageSize().getWidth() - 10F,
            10F,
            0F);
        coverCreatedCb.endText();
        document.newPage();

    }

    private PdfPTable createLeftPage(UserInput ui, OneDayAreaInfo odpoint,
            Document document, PdfPCell[][] cells, int startIdx)
            throws DocumentException {
        float tojisiro = 0F;
        if (ui.isTojisiro()) {
            tojisiro = Util.mm2point(ui.getTojisiroSize());
        }
        PdfPTable table = new PdfPTable(4);
        table
            .setTotalWidth(pageSize[ui.getSizeOfPaper()].getWidth() - tojisiro);
        int width[] = { 25, 25, 25, 25 };
        table.setWidths(width);
        // table.getDefaultCell().setBorder(Rectangle.NO_BORDER);
        for (int i = 0; i < 4; i++) {
            table.addCell(cells[startIdx * 8 + i][0]);
        }
        for (int i = 0; i < 4; i++) {
            table.addCell(cells[startIdx * 8 + i][1]);
        }
        PdfPCell cell = new PdfPCell();
        cell.setFixedHeight(odpoint.getVrticalHeight());
        for (int i = 0; i < odpoint.getVrticalNum(); i++) {
            for (int j = 0; j < 4; j++) {
                if (j == 0) {
                    String time =
                        String.valueOf(ui.getVirticalStartTime() + i) + ":00";
                    PdfPCell timeCell =
                        new PdfPCell(new Phrase(time, new Font(
                            jbf,
                            6F,
                            Font.BOLD,
                            Constant.VIRTICAL_TIME_COLOR)));
                    timeCell.setBorder(Rectangle.NO_BORDER);
                    timeCell.setVerticalAlignment(Rectangle.ALIGN_TOP);
                    timeCell.setHorizontalAlignment(Rectangle.ALIGN_RIGHT);
                    table.addCell(timeCell);
                } else {
                    cell.setBorderColor(BaseColor.BLUE);
                    table.addCell(cell);
                }
            }
        }
        return table;
    }

    private PdfPTable createRightPage(UserInput ui, OneDayAreaInfo odpoint,
            Document document, PdfPCell[][] cells, int startIdx)
            throws DocumentException {
        float tojisiro = 0F;
        if (ui.isTojisiro()) {
            tojisiro = Util.mm2point(ui.getTojisiroSize());
        }
        PdfPTable table = new PdfPTable(4);
        table
            .setTotalWidth(pageSize[ui.getSizeOfPaper()].getWidth() - tojisiro);
        int width[] = { 25, 25, 25, 25 };
        table.setWidths(width);
        // table.getDefaultCell().setBorder(Rectangle.NO_BORDER);
        for (int i = 4; i < 8; i++) {
            table.addCell(cells[startIdx * 8 + i][0]);
        }
        for (int i = 4; i < 8; i++) {
            table.addCell(cells[startIdx * 8 + i][1]);
        }
        PdfPCell cell = new PdfPCell();
        cell.setFixedHeight(odpoint.getVrticalHeight());
        for (int i = 0; i < odpoint.getVrticalNum(); i++) {
            for (int j = 0; j < 4; j++) {
                cell.setBorderColor(BaseColor.BLUE);
                table.addCell(cell);
            }
        }
        return table;
    }

    private void plotScheduleLeftPage(UserInput ui, EventManager em,
            OneDayAreaInfo odpoint, Calendar cal, PdfWriter writer) {
        for (int i = 0; i < 3; i++) {
            String key =
                String.valueOf((new StringBuilder(String.valueOf(cal
                    .get(Calendar.YEAR)))).append(
                    String.valueOf(cal.get(Calendar.DAY_OF_YEAR))).toString());
            List<Event> eventList = em.getEventList().get(key);
            if (eventList != null) {
                PdfContentByte pcb = writer.getDirectContent();
                for (Iterator<Event> iterator = eventList.iterator(); iterator
                    .hasNext();) {
                    Event event = iterator.next();
                    pcb.setLineWidth(Constant.IVENT_TIME_LINE_STROKE);
                    pcb.setColorStroke(event.getCalenderColor());

                    float startPosY =
                        odpoint.getStartTimePositionHeight(
                            ui,
                            event.getWhen(),
                            cal.getTime());
                    float endPosY =
                        startPosY
                            - (odpoint.getTime2Height(
                                ui,
                                event.getWhen(),
                                cal.getTime()) - Constant.IVENT_TIME_LINE_STROKE / 2.0F);
                    float x =
                        ((float) (i + 1) * odpoint.getOneDayRect().getWidth()) + 12F;
                    pcb.moveTo(x, startPosY);
                    pcb.lineTo(x, endPosY);
                    pcb.stroke();
                    pcb.moveTo(x - 2F, startPosY);
                    pcb.lineTo(x + 2F, startPosY);
                    pcb.stroke();
                    pcb.moveTo(x - 2F, endPosY);
                    pcb.lineTo(x + 2F, endPosY);
                    pcb.stroke();
                    pcb.beginText();
                    pcb.setFontAndSize(jbf, ui.getEventFontSize());
                    pcb.showTextAligned(
                        PdfContentByte.ALIGN_LEFT,
                        event.getEventTitle(),
                        x + 2F,
                        startPosY - ui.getEventFontSize(),
                        0);
                    pcb.endText();
                    pcb.stroke();
                }

            }
            cal.add(Calendar.DAY_OF_YEAR, 1);
        }
    }

    private void plotScheduleRightPage(UserInput ui, EventManager em,
            OneDayAreaInfo odpoint, Calendar cal, PdfWriter writer) {
        float tojisiro = 0F;
        if (ui.isTojisiro()) {
            tojisiro = Util.mm2point(ui.getTojisiroSize());
        }
        for (int i = 0; i < 4; i++) {
            String key =
                String.valueOf((new StringBuilder(String.valueOf(cal
                    .get(Calendar.YEAR)))).append(
                    String.valueOf(cal.get(Calendar.DAY_OF_YEAR))).toString());
            List<Event> eventList = em.getEventList().get(key);
            if (eventList != null) {
                PdfContentByte pcb = writer.getDirectContent();
                for (Iterator<Event> iterator = eventList.iterator(); iterator
                    .hasNext();) {
                    Event event = iterator.next();
                    pcb.setLineWidth(Constant.IVENT_TIME_LINE_STROKE);
                    pcb.setColorStroke(event.getCalenderColor());
                    float startPosY =
                        odpoint.getStartTimePositionHeight(
                            ui,
                            event.getWhen(),
                            cal.getTime());
                    float endPosY =
                        startPosY
                            - (odpoint.getTime2Height(
                                ui,
                                event.getWhen(),
                                cal.getTime()) - Constant.IVENT_TIME_LINE_STROKE / 2.0F);
                    float x =
                        ((float) i * odpoint.getOneDayRect().getWidth())
                            + tojisiro
                            + 12F;
                    pcb.moveTo(x, startPosY);
                    pcb.lineTo(x, endPosY);
                    pcb.stroke();
                    pcb.moveTo(x - 2F, startPosY);
                    pcb.lineTo(x + 2F, startPosY);
                    pcb.stroke();
                    pcb.moveTo(x - 2F, endPosY);
                    pcb.lineTo(x + 2F, endPosY);
                    pcb.stroke();
                    pcb.beginText();
                    pcb.setFontAndSize(jbf, ui.getEventFontSize());
                    pcb.showTextAligned(
                        PdfContentByte.ALIGN_LEFT,
                        event.getEventTitle(),
                        x + 2F,
                        startPosY - ui.getEventFontSize(),
                        0);
                    pcb.endText();
                    pcb.stroke();
                }

            }
            cal.add(Calendar.DAY_OF_YEAR, 1);
        }
    }

    private PdfPTable createDayInfo(UserInput ui, OneDayAreaInfo odpoint,
            EventManager em, Date createDate) throws Exception {
        Calendar cal = Calendar.getInstance();
        cal.setTime(createDate);
        PdfPTable dateAreaTable = new PdfPTable(3);
        dateAreaTable.setTotalWidth(odpoint.getOneDayRect().getWidth());
        int width[] = { 35, 40, 25 };
        dateAreaTable.setWidths(width);
        String dayOfWeek =
            Constant.DAY_OF_WEEK[cal.get(Calendar.DAY_OF_WEEK) - 1];
        String date = String.valueOf(cal.get(Calendar.DATE));
        PdfPCell dayCell = null;
        PdfPCell dateCell = null;
        switch (cal.get(Calendar.DAY_OF_WEEK)) {
        case 1: // '\001'
            dayCell =
                new PdfPCell(new Phrase(dayOfWeek, new Font(
                    bf,
                    Constant.DAY_FONT_SIZE[ui.getSizeOfPaper()],
                    Font.NORMAL,
                    Constant.SUNDAY_COLOR)));
            dateCell =
                new PdfPCell(new Phrase(date, new Font(
                    bf,
                    Constant.DATE_FONT_SIZE[ui.getSizeOfPaper()],
                    Font.NORMAL,
                    Constant.SUNDAY_COLOR)));
            break;

        case 7: // '\007'
            dayCell =
                new PdfPCell(new Phrase(dayOfWeek, new Font(
                    bf,
                    Constant.DAY_FONT_SIZE[ui.getSizeOfPaper()],
                    Font.NORMAL,
                    Constant.SATURDAY_COLOR)));
            dateCell =
                new PdfPCell(new Phrase(date, new Font(
                    bf,
                    Constant.DATE_FONT_SIZE[ui.getSizeOfPaper()],
                    Font.NORMAL,
                    Constant.SATURDAY_COLOR)));
            break;

        default:
            dayCell =
                new PdfPCell(new Phrase(dayOfWeek, new Font(
                    bf,
                    Constant.DAY_FONT_SIZE[ui.getSizeOfPaper()],
                    Font.NORMAL,
                    Constant.WEEKDAY_COLOR)));
            dateCell =
                new PdfPCell(new Phrase(date, new Font(
                    bf,
                    Constant.DATE_FONT_SIZE[ui.getSizeOfPaper()],
                    Font.NORMAL,
                    Constant.WEEKDAY_COLOR)));
            break;
        }
        dayCell.setBorder(Rectangle.NO_BORDER);
        dayCell.setHorizontalAlignment(Element.ALIGN_LEFT);
        dayCell.setVerticalAlignment(Element.ALIGN_BOTTOM);
        dateAreaTable.addCell(dayCell);
        String key =
            String.valueOf((new StringBuilder(String.valueOf(cal
                .get(Calendar.YEAR)))).append(
                String.valueOf(cal.get(Calendar.DAY_OF_YEAR))).toString());
        List<Event> eventList = em.getEventListHolyday().get(key);
        PdfPCell holidayCell = null;
        if (eventList != null) {
            Event event = (Event) eventList.get(0);
            if (event != null) {
                String holiday = event.getEventTitle();
                holidayCell =
                    new PdfPCell(new Phrase(holiday, new Font(
                        jbf,
                        Constant.HOLIDAY_NAME_FONT_SIZE[ui.getSizeOfPaper()],
                        Font.NORMAL,
                        Constant.HOLIDAY_COLOR)));
            }
        } else {
            holidayCell = new PdfPCell(new Phrase(""));
        }
        holidayCell.setHorizontalAlignment(Element.ALIGN_CENTER);
        holidayCell.setVerticalAlignment(Element.ALIGN_BOTTOM);
        holidayCell.setBorder(Rectangle.NO_BORDER);
        dateAreaTable.addCell(holidayCell);
        dateCell.setBorder(Rectangle.NO_BORDER);
        dateCell.setHorizontalAlignment(Element.ALIGN_RIGHT);
        dateCell.setVerticalAlignment(Element.ALIGN_BOTTOM);
        dateAreaTable.addCell(dateCell);
        return dateAreaTable;
    }

    private PdfPCell createDayEvent(OneDayAreaInfo odpoint, EventManager em,
            Date createDate) {
        PdfPCell oneDayEventCell = new PdfPCell();
        oneDayEventCell.setFixedHeight(odpoint.getEventEreaHeight()
            - OneDayAreaInfo.DAYINFO_HEIGHT);
        oneDayEventCell.setBorder(Rectangle.BOX);
        Calendar cal = Calendar.getInstance();
        cal.setTime(createDate);
        String key =
            String.valueOf((new StringBuilder(String.valueOf(cal
                .get(Calendar.YEAR)))).append(
                String.valueOf(cal.get(Calendar.DAY_OF_YEAR))).toString());
        List<Event> eventListDay = em.getEventDateOnlyList().get(key);
        if (eventListDay != null) {
            int counter = 1;
            for (Iterator<Event> iterator = eventListDay.iterator(); iterator
                .hasNext();) {
                Event event = (Event) iterator.next();
                String title;
                if (event.isDateOnly())
                    title = event.getEventTitle();
                else
                    title =
                        (new StringBuilder(String.valueOf(event
                            .getWhenTimeString()))).append(
                            event.getEventTitle()).toString();
                oneDayEventCell.addElement(new Phrase(title, new Font(
                    jbf,
                    6F,
                    Font.NORMAL,
                    event.getCalenderColor())));
                if (++counter > odpoint.getOneDayEventNum())
                    break;
            }
        }
        return oneDayEventCell;
    }
}
